package com.rcmd.agent;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

import org.apache.log4j.Logger;

import com.rcmd.cmd.CMDCommand;
import com.rcmd.cmd.Command;
import com.rcmd.cmd.DataCommand;
import com.rcmd.cmd.ErrorCommand;
import com.rcmd.cmd.FileCommand;
import com.rcmd.cmd.FileCommand.ACTION;
import com.rcmd.cmd.FileDataCommand;
import com.rcmd.cmd.FileIO;
import com.rcmd.cmd.InitCommand;
import com.rcmd.cmd.WireIO;

public class Proc extends Thread{
	
	private Socket client ;
	private ObjectInput sock_in ; 
	private ObjectOutput sock_out  ;
	private Process process ;
	private InputStream pro_in ;
	private InputStream pro_err_in ;

	private PrintWriter pro_out    ;

	private Logger log ;
	public Proc(Socket s) throws IOException{
		this.client = s; 

		sock_in= new ObjectInputStream(client.getInputStream()) ;
    	sock_out = new ObjectOutputStream(client.getOutputStream());
    	log = Logger.getLogger(String.valueOf(s.getPort()));
    	log.addAppender(util.Log4jUtil.getFileAppender("server-"+String.valueOf(s.getPort())+".log"));
	}
	public void initProcess(String cmd) throws IOException{
		process = Runtime.getRuntime().exec(cmd);
		pro_in =process.getInputStream() ;
		pro_err_in = process.getErrorStream() ;
		pro_out	= new PrintWriter(process.getOutputStream());
		new Thread(new ProcRunnable(pro_in)).start();
		new Thread(new ProcRunnable(pro_err_in)).start();
		debugPrint("process " +cmd +" started");
	}
	public void run(){
		try{
			handleClientCMD();
			
		}catch(Throwable e){
			debugPrint("exception when handle cmd") ;
			debugPrint(e) ;
			try {
				WireIO.write(sock_out, new ErrorCommand(e));
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				//e1.printStackTrace();
			}
		}finally{
			debugPrint("enter finally, to clean resorce");
			clean();
		}
	}
	protected void clean(){
		try {
			if( sock_in!=null){
				sock_in.close();
				debugPrint("close socket input stream");
			}
			if( sock_out!=null){
				sock_out.close();
				debugPrint("close socket output stream");

			}
			this.client.close();

			if( process!=null){
				process.destroy();
			}
			if( pro_in != null ) pro_in.close() ;
			debugPrint("close process input stream");

			if( pro_err_in != null ) pro_err_in.close();
			if( pro_out != null) pro_out.close();
		} catch (IOException e) {
			debugPrint(e) ;
		}
		debugPrint("resource is clean!") ;
	}
	public void handleClientCMD() throws IOException{
		
		
		while(true){
			Command cmd = WireIO.read(sock_in) ;

			switch (cmd.getType()){
				case Command.INIT:
					initProcess(((InitCommand)cmd).getBash());
					break;
				case Command.CMDCOMMAND:
					CMDCommand cmd_remote = (CMDCommand)cmd ;
					
					pro_out.println(cmd_remote.getCmd()) ;
					debugPrint("" );
					debugPrint("++++++++++++ " + cmd_remote.getCmd() + " +++++++++++++++++++") ;
					//debugPrint("cmd: " + cmd_remote.getCmd()) ;
					pro_out.println("") ;
					pro_out.flush();
					if( cmd_remote.getCmd().equals("Q!")){
						clean();
						return ;
					}
					break ;
				case Command.FILE:
					FileCommand fcmd = (FileCommand)cmd;
					if(fcmd.getAction() == ACTION.GET){
						FileIO.writeFile2Pipe(fcmd	, sock_out,log) ;
					}
					break;
				case Command.FILEDATA:
					FileDataCommand fcmdd = (FileDataCommand)cmd ;
					FileIO.readFileFromPipe(fcmdd, sock_in ,log) ;
					break ;
				default:
					break;
			}
		}
	}
	
	public void debugPrint(String s){
		//System.out.println("debug: " + s) ;
	    log.info(s) ;
	}
	public void debugPrint(Throwable  s){
		//System.out.println("debug: " + s.getMessage()) ;
	    log.error(s.getMessage(),s) ;
		//s.printStackTrace(System.out) ;
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
	}
	public Socket getClient() {
		return client;
	}
	public void setClient(Socket client) {
		this.client = client;
	}


class ProcRunnable implements Runnable {
	InputStream process_inputstream = null;
	String name;
	public ProcRunnable(InputStream inputStream) {
		this.process_inputstream = inputStream;
	}
	@Override
	public void run() {
		Scanner scanner = new Scanner(process_inputstream);

		try {
		while (scanner.hasNextLine()) {
			String dd = scanner.nextLine() ;
			DataCommand data = new DataCommand(dd.getBytes()) ;
			debugPrint( new String(dd.getBytes())) ;
//			System.out.println("pro: " + String.valueOf(data.getData())) ;
			WireIO.write( sock_out, data	) ;
		}
		System.out.println("enf of loop in server for the pro input") ;

			process_inputstream.close();
		} catch (IOException e) {
			debugPrint(e) ;
		}
		finally{
			scanner.close();
			debugPrint("end of thread();");

		}
	}
}
}
